/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package com.googlecode.ivybeans.facade;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.text.ParseException;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.util.Checks;

/**
 * An IvyLibrary is a library of jars, resolved using Apache Ivy based on
 * dependencies described by a module descriptor (usually an Ivy file), and
 * optionnaly specific Ivy settings. The module configurations to put in the
 * library are also configurable.
 * 
 * @author Xavier Hanin, lforet
 */
public class IvyLibrary {

    private URL settingsURL = getDefaultSettingsURL();
    private URL descriptorURL;
    private Ivy ivy;

    public IvyLibrary() {
        ivy = new Ivy();
    }

//    public IvyLibrary(URL descriptorURL) {
//        this(getDefaultSettingsURL(), descriptorURL);
//    }
//
//    public IvyLibrary(URL settingsURL, URL descriptorURL) {
//        Checks.checkNotNull(settingsURL, "settingsURL");
//        Checks.checkNotNull(descriptorURL, "descriptorURL");
//        ivy = new Ivy();
//        this.settingsURL = settingsURL;
//        this.descriptorURL = descriptorURL;
//    }

    /**
     * Returns the Ivy instance used in this library.
     * @return the Ivy instance used in this library, may be <code>null</code>.
     */
    public Ivy getIvyInstance() {
        /* TODO: move Ivy instance loading responsibility out of this class,
         * it makes a call to resolve required to have an ivy instance initialised.
         */
        return ivy;
    }

    public List<String> listOrganizations() {
        return listTokenValues(IvyPatternHelper.ORGANISATION_KEY, Collections.EMPTY_MAP);
    }

    public List<String> listModules(String organization) {
        return listTokenValues(
                IvyPatternHelper.MODULE_KEY,
                Collections.singletonMap(IvyPatternHelper.ORGANISATION_KEY, organization));
    }

    public List<String> listRevisions(String organization, String module) {
        Map otherTokens = new HashMap();
        otherTokens.put(IvyPatternHelper.ORGANISATION_KEY, organization);
        otherTokens.put(IvyPatternHelper.MODULE_KEY, module);
        return listTokenValues(
                IvyPatternHelper.REVISION_KEY,
                otherTokens);
    }

    public List<String> listConfigurations() throws IvyLibraryException {
        if (settingsURL == null || descriptorURL == null) {
            return Collections.EMPTY_LIST;
        }
        configure();
        try {
            ModuleDescriptor md = ModuleDescriptorParserRegistry.getInstance().parseDescriptor(
                    ivy.getSettings(), descriptorURL, new URLResource(descriptorURL), false);
            return Arrays.asList(md.getConfigurationsNames());
        } catch (ParseException e) {
            throw new IvyLibraryParsingException(e.getMessage(), settingsURL, e);
        } catch (IOException e) {
            throw new IvyLibraryIOException(e.getMessage(), settingsURL, e);
        }
    }

    /**
     * Lists the values a token can take given values for a set of other tokens.
     *
     * @param token the token for which values should be returned
     * @param otherTokens a Map of token to values
     * @return the list of possible values, is never <code>null</code>
     */
    public List<String> listTokenValues(String token, Map otherTokens) {
        String[] vals = ivy.listTokenValues(token, otherTokens);
        if (vals != null) {
            return Arrays.asList(vals);
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    /**
     * Resolves this IvyLibrary, returning an instance of ResolvedIvyLibrary
     * representing the resolved dependencies.
     * 
     * @param confs
     *            an array of module configurations to put in the library. Must
     *            not be <code>null</code>.
     *
     * @return an instance of {@link ResolvedIvyLibrary}
     * 
     * @throws IvyLibraryException
     */
    public ResolvedIvyLibrary resolve(String[] confs) throws IvyLibraryException {
        if (settingsURL == null || descriptorURL == null) {
            return ResolvedIvyLibrary.EMPTY_RESOLVED;
        }
        configure();
        try {
            ResolveReport report = ivy.resolve(descriptorURL, new ResolveOptions().setConfs(confs));
            return new ResolvedIvyLibrary(ivy, report);
        } catch (ParseException e) {
            throw new IvyLibraryParsingException(e.getMessage(), descriptorURL, e);
        } catch (IOException e) {
            throw new IvyLibraryIOException(e.getMessage(), descriptorURL, e);
        }
    }

    public URL getDescriptorURL() {
        return descriptorURL;
    }

    public URL getSettingsURL() {
        return settingsURL;
    }

    public static URL getDefaultSettingsURL() {
        return IvySettings.getDefaultSettingsURL();
    }

    public void setDescriptorURL(URL descriptorURL) {
        this.descriptorURL = descriptorURL;
    }

    public void setSettingsURL(URL settingsURL) throws IvyLibraryException {
        this.settingsURL = (settingsURL != null) ? settingsURL : getDefaultSettingsURL();
    }

    public void setOut(PrintWriter out) {
        ivy.getLoggerEngine().pushLogger(new SimpleIvyLogger(out));
    }
    
    private void configure() {
         try {
            ivy.configure(settingsURL);
        } catch (ParseException e) {
            throw new IvyLibraryParsingException(e.getMessage(), settingsURL, e);
        } catch (IOException e) {
            throw new IvyLibraryIOException(e.getMessage(), settingsURL, e);
        }
    }
}
